<?php

/**
 * Blibli  
 */
class PlatformAPIClient_Blibli extends PlatformAPIClient_Abstract {
    
    /**
     * client
     * @var type 
     */
    private $_client = null;
    
    /**
     * api config
     * @var type 
     */
    private $_apiConfig = null;
    
    /**
     * token cache key
     * @var type 
     */
    private $_tokenCacheKey = 'blibli:token';
    
    /**
     * API root
     */
    const API_ROOT = 'https://api.blibli.com/v2';
    
    /**
     * 初始化
     * @param type $option
     */
    public function __construct($option = array()) {
        
        parent::__construct($option);

        if (!empty($option['debug_mode'])) {
            define('BLIBLI_DEBUG', true);
        }
        require_once dirname(dirname(__FILE__)) . '/third-lib/blibli-sdk/BlibliMerchantClient.php';
        
        $client = new BlibliMerchantClient();
        $cache = PlatformAPIClient::getCacheHandler();
        $cacheKey = $this->_tokenCacheKey;
        $accessToken = $cache->get($cacheKey);
        if (!$accessToken) {
            ###----------------------------------------------------###
            ###-------------- REQUEST TOKEN SAMPLE ----------------###
            ###----------------------------------------------------###
            $token_request = new TokenRequest();
            $token_request->setApiUsername($this->_option['api_username']);
            $token_request->setApiPassword($this->_option['api_passwd']);
            $token_request->setMtaUsername($this->_option['mta_username']);
            $token_request->setMtaPassword($this->_option['mta_passwd']);
            $token_request->setPlatformName($this->_option['platform_name']);
            $token_request->setTimeoutSecond($this->_option['timeout_time']);

            //put Get Token API url here
            $url = "https://api.blibli.com/v2/oauth/token";
            //call Get Token API
            $response = $client->getToken($url, $token_request);
        //    echo $response, PHP_EOL, PHP_EOL;
            $data = json_decode($response, true);
            $accessToken = $data['access_token'];
            $expireIn = $data['expires_in'];
            $cache->set($cacheKey, $accessToken, $expireIn - 600);
        }
        
        $config = new ApiConfig();
        $config->setToken($accessToken);
        $config->setSecretKey($this->_option['api_secret']); 
        $config->setMtaUsername($this->_option['mta_username']);
        $config->setBusinessPartnerCode($this->_option['partner_code']);
        $config->setPlatformName($this->_option['platform_name']);
        $config->setTimeoutSecond($this->_option['timeout_time']);
        
        $this->_client = $client;
        $this->_apiConfig = $config;
    }

    /**
     * 获取订单列表
     * @param type $conditions
     */
    public function getOrderList($conditions) {
        
        $url = self::API_ROOT."/proxy/mta/api/businesspartner/v1/order/orderList";

        $params = array(
//            "status" => "FP",
//            "filterStartDate" => date("Y-m-d H:i:s", time() - 86400*3),
//            'filterEndDate' => date('Y-m-d H:i:s'),
            'page' => 0,
            'size' => 10,
        );
        $params = array_merge($params, $conditions);

        //invoke [Get] Order List API
        $response = $this->_client->invokeGet($url, $params, $this->_apiConfig);
        $data = $this->_parseResponse($response, 'get blibli orders');

        $tmpOrders = [];
        // 结果是一个商品一条记录，按 order 维度重新组织
        $itemFields = ['orderItemNo', 'productName', 'merchantSku', 'productPrice', 'qty'];
        foreach ($data['content'] as $orderItem) {
            $orderId = $orderItem['orderNo'];
            if (!isset($tmpOrders[$orderId])) {
                $orderFields = array_diff(array_keys($orderItem), $itemFields);
                $tmpOrders[$orderId] = self::ArrFetch($orderItem, $orderFields);
                $tmpOrders[$orderId]['items'] = [];
                $tmpOrders[$orderId]['total_price'] = 0;
            }
            $tmpOrders[$orderId]['items'][] = self::ArrFetch($orderItem, $itemFields);
            $tmpOrders[$orderId]['total_price'] += $orderItem['qty'] * intval($orderItem['productPrice']);
        }
        foreach ($tmpOrders as $orderId => $orderInfo) {
            $orders[] = $orderInfo;
        }
        unset($tmpOrders);
        return $orders;
    }
    
    /**
     * 获取指定订单详情
     * 注意：blibli 不能直接获取某个订单的数据，只能具体到订单商品项
     * @param array $orderId
     *  + orderNo
     *  + orderItemNo
     * @return type
     */
    public function getOrder($orderId) {
        
        if (!is_array($orderId) || empty($orderId['orderNo']) || empty($orderId['orderItemNo'])) {
            throw new Exception('bad param of blibli orderId');
        }
        
        $url = self::API_ROOT."/proxy/mta/api/businesspartner/v1/order/orderDetail";
        $params = $orderId;
        $response = $this->_client->invokeGet($url, $params, $this->_apiConfig);
        $data = $this->_parseResponse($response, 'get order detail');
        return !empty($data['value']) ? $data['value'] : [];
    }
    
    /**
     * 解析返回结果
     * 
     * @param type $response
     * @param type $actionName
     * @return type
     * @throws Exception
     */
    private function _parseResponse($response, $actionName = '') {
        
        if (!$response) {
            throw new Exception($actionName.' failed: empty response');
        }
        $data = json_decode($response, true);
        // error
        if (!empty($data['error'])) {
            // reset token
            if ($data['error'] === 'invalid_token') {
                $cache = PlatformAPIClient::getCacheHandler();
                $cacheKey = $this->_tokenCacheKey;
                $cache->delete($cacheKey);
            }
            throw new Exception($actionName.' failed: '.$data['error']);
        }
        return $data;
    }
    
    /**
     * 获取指定商品基本信息
     * @param type $sku
     */
    public function getProductSummary($sku) {

        $url = self::API_ROOT."/proxy/mta/api/businesspartner/v2/product/getProductList";
        $body = array(
            'gdnSku' => trim($sku['sku_id']),
        );
        $params = array();
        $response = $this->_client->invokePost($url, $params, $body, $this->_apiConfig);
        $data = $this->_parseResponse($response, 'get product summary');
        return !empty($data['content']) ? $data['content'][0] : [];
    }

    /**
     * 获取 指定商品信息（详情）
     * @param type $sku
     */
    public function getProduct($sku) {
        
        $url = self::API_ROOT."/proxy/mta/api/businesspartner/v1/product/detailProduct";
        $params = array(
            'gdnSku' => trim($sku['sku_id']),
        );
        $response = $this->_client->invokeGet($url, $params, $this->_apiConfig);
        $data = $this->_parseResponse($response, 'get product detail');
        return !empty($data['value']) ? $data['value'] : [];
    }

    /**
     * 设置 sku 库存
     * 注意：blibli 的设置库存接口，只能设置 变化量；故需要先获取当前值，计算差值再提交
     * @param type $sku
     * @param type $stock
     */
    public function setStock($sku, $stock) {
        
        // 获取现在 sku 信息，计算 stock 差值
        $skuInfo = $this->getProductSummary($sku);
        if (empty($skuInfo) || !isset($skuInfo['stockAvailableLv2'])) {
            throw new Exception('bad sku given!');
        }
        $stockDiff = $stock - $skuInfo['stockAvailableLv2'];
        // 相同，无需修改
        if ($stockDiff === 0) {
            return true;
        }

        $url = self::API_ROOT."/proxy/mta/api/businesspartner/v1/product/updateProduct";
        $body = array(
            'merchantCode' => $this->_option['partner_code'],
            'productRequests' => [
                [
                    'gdnSku' => trim($sku['sku_id']),
                    'stock' => intval($stockDiff),
                    "minimumStock" => null,
                    "price" => null,
                    "salePrice" => null,
                    "buyable" => null,
                    "displayable" => null,
                ]
            ],
        );
        $params = array();
        $response = $this->_client->invokePost($url, $params, $body, $this->_apiConfig);
        return $this->_parseResponse($response, 'set sku stock');
    }
    
    /**
     * 从数组中提取指定字段，返回新数组
     * @param array $arr
     * @param array $fields
     * @return array
     */
    private static function ArrFetch($arr, $fields) {
        
        $newArr = [];
        foreach ($fields as $field) {
            $newArr[$field] = isset($arr[$field]) ? $arr[$field] : null;
        }
        return $newArr;
    }
}